using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using FreeSwitch.EventSocket.Parser;

namespace FreeSwitch.EventSocket
{
	public class EventChannel : IEventChannel
	{
		private readonly byte[] _buffer = new byte[65535];
		private readonly Socket _client;
		private readonly IPEndPoint _endPoint;
		private readonly ILogger _logger = LogManager.GetLogger(typeof (EventChannel));
		private readonly MessageParser _parser;
		private NetworkStream _stream;

		public EventChannel(IPEndPoint freeSwitchAddress)
			: this(new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp), freeSwitchAddress)
		{
		}

		public EventChannel(Socket socket) : this(socket, (IPEndPoint) socket.RemoteEndPoint)
		{
		}

		private EventChannel(Socket socket, IPEndPoint endPoint)
		{
			_client = socket;
			_endPoint = endPoint;

			_parser = new MessageParser();
			_parser.ExceptionThrown += OnParserException;
			_parser.MessageReceived += OnMessage;
			_parser.Start();
		}

		#region IEventChannel Members

		public bool IsConnected
		{
			get { return _client != null && _client.Connected; }
		}

		public void Send(string data)
		{
			byte[] bytes = Encoding.ASCII.GetBytes(data);
			_logger.Debug("Sending: " + data.TrimEnd('\n'));
			_stream.BeginWrite(bytes, 0, bytes.Length, null, null);
		}

		public event EventHandler<MessageEventArgs> MessageReceived = delegate { };
		public event EventHandler Disconnected = delegate { };

		#endregion

		private void OnParserException(object sender, ExceptionEventArgs e)
		{
			_logger.Error("Parser failed.", e.Exception);
		}

		private void OnMessage(object sender, MessageEventArgs e)
		{
			_logger.Debug(e.Message.ToString());
			MessageReceived(this, e);
		}

		public void Start()
		{
			if (!_client.Connected)
			{
				_client.Connect(_endPoint);
				_stream = new NetworkStream(_client);
			}
			Read(0);
			Connected(this, EventArgs.Empty);
		}

		private void Read(int offset)
		{
			try
			{
				_stream.BeginRead(_buffer, offset, _buffer.Length - offset, OnRead, null);
			}
			catch (Exception err)
			{
				_logger.Debug("Failed to begin reading", err);
				Disconnect();
			}
		}

		private void OnRead(IAsyncResult ar)
		{
			int bytesRead;
			try
			{
				bytesRead = _stream.EndRead(ar);
				_logger.Debug("Bytes read: " + bytesRead);
				if (bytesRead == 0)
				{
					_logger.Warning("Remote end closed connection.");
					Disconnect();
					return;
				}
			}
			catch (Exception err)
			{
				_logger.Debug("Failed to end read.", err);
				Disconnect();
				return;
			}

			try
			{
				_parser.Append(_buffer, 0, bytesRead);
			}
			catch (Exception err)
			{
				_logger.Error("Failed to parse buffer", err);
			}

			Read(0);
		}

		private void Disconnect()
		{
			_client.Close();
			Disconnected(this, EventArgs.Empty);
		}

		/// <summary>
		/// Got connection to freeswitch.
		/// </summary>
		public event EventHandler Connected = delegate { };
	}
}